#include "core/kernel.h"
#include "core/log.h"

namespace core {
	Kernel::Kernel() {
		mStop.add(&Kernel::onStop, this);
	}

	Kernel::~Kernel() {
	}

	class InternalTask: public tbb::task {
	public:
		InternalTask(osg::ref_ptr<Task> parent) {
			mParent = parent;
		}

		tbb::task* execute() {
			mParent->tick();
			return NULL;
		}

		osg::ref_ptr<Task> mParent;
	};

	void Kernel::addTask(Task* t, bool repeating, bool parallel) {
		ScopedLock lock(mMutex);
		if (parallel)
			mParallelTasks.push_back(std::make_pair(repeating, TaskPtr(t)));
		else
			mTasks.push_back(std::make_pair(repeating, TaskPtr(t)));
	}

	void Kernel::removeTask(Task* t) {
		ScopedLock lock(mMutex);
		for (TaskIterator it = mTasks.begin(); it != mTasks.end(); ++it) {
			if (it->second.get() == t) {
				mTasks.erase(it);
				return;
			}
		}

		for (TaskIterator it = mParallelTasks.begin(); it != mParallelTasks.end(); ++it) {
			if (it->second.get() == t) {
				mParallelTasks.erase(it);
				return;
			}
		}
	}

	void Kernel::run() {
		tbb::task_scheduler_init init(tbb::task_scheduler_init::automatic);
		mRunning = true;
		
		while (mRunning) {
			mRootTask = new (tbb::task::allocate_root()) RootTask(this);
			tbb::task::spawn_root_and_wait(*mRootTask);

			TaskIterator it = mTasks.begin();
			TaskIterator current;
			while (it != mTasks.end()) {
				current = it++;
				current->second->tick();
				if (current->first == false)
					mTasks.erase(current);
			}
		}
	}

	void Kernel::onStop() {
		mRunning = false;
	}

	Kernel::RootTask::RootTask(Kernel* parent) {
		mParent = parent;
	}

	tbb::task* Kernel::RootTask::execute() {
		int numTasks = 0;
		tbb::task_list list;
		tbb::task* c = new (allocate_continuation()) tbb::empty_task();

		TaskIterator it = mParent->mParallelTasks.begin();
		TaskIterator current;

		while (it != mParent->mParallelTasks.end()) {
			current = it++;
			list.push_back(*new (c->allocate_child()) InternalTask(current->second));
			++numTasks;
			if (current->first == false)
				mParent->mParallelTasks.erase(current);
		}

		if (numTasks > 0) {
			c->set_ref_count(numTasks);
			c->spawn(list);
		}
		else
			spawn(*c);

		return NULL;
	}
}
